{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "view-in-github"
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/Aithu-Snehith/End-to-End-Learning-of-Communications-Systems-Without-a-Channel-Model/blob/master/2_pam_model.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "iw733J8hoIZN"
   },
   "source": [
    "## Importing Required Packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "YD6Q1OO06bA2"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "import matplotlib.pyplot as plt\n",
    "from tensorflow import keras\n",
    "from tensorflow.keras.layers import *\n",
    "from sklearn import preprocessing\n",
    "import tensorflow.keras.backend as K\n",
    "from sklearn.metrics import mean_squared_error"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "0l6o6_eHoMPO"
   },
   "source": [
    "## Hyper Parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Mamp_sR48wBd"
   },
   "outputs": [],
   "source": [
    "msg_total = 2\n",
    "channel = 4\n",
    "epochs = 5000\n",
    "sigma = 1e-4\n",
    "batch_size = 1024"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "4fcMrH6IoP9t"
   },
   "source": [
    "## Defiing required functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "cUhpbsruIeoE"
   },
   "outputs": [],
   "source": [
    "def perturbation(x):\n",
    "    w = K.random_normal(shape = (channel,2), mean=0.0,stddev=sigma**0.5,dtype=None)\n",
    "    xp = ((1-sigma)**0.5)*x + w\n",
    "    return xp\n",
    "\n",
    "def loss_tx(y_true, y_pred):\n",
    "    return -y_true*y_pred\n",
    "\n",
    "def get_policy(inp):\n",
    "    xp = inp[0]\n",
    "    x = inp[1]\n",
    "    w = xp - x\n",
    "    policy = -K.sum(w*w)\n",
    "    return policy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "5EBpvCvvoTeX"
   },
   "source": [
    "# Transmitter \n",
    "\n",
    "## Defining Architecture"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 88
    },
    "colab_type": "code",
    "id": "62RZYX6p-Q91",
    "outputId": "27e95718-a3a4-4a20-afcf-8c956a4831b2"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Colocations handled automatically by placer.\n"
     ]
    }
   ],
   "source": [
    "tx_inp = Input((1,))\n",
    "embbedings_layer = Dense(msg_total, activation = 'relu')(tx_inp)\n",
    "layer_dense = Dense(2*channel, activation = 'relu')(embbedings_layer)\n",
    "to_complex = Reshape((channel,2))(layer_dense)\n",
    "x = Lambda(lambda x: keras.backend.l2_normalize(x))(to_complex)\n",
    "xp = Lambda(perturbation)(to_complex)\n",
    "policy = Lambda(get_policy)([xp,x])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "RVB6NOrCoZHG"
   },
   "source": [
    "## Declaring Models"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 408
    },
    "colab_type": "code",
    "id": "tdq7Oou4J1ZL",
    "outputId": "89e684af-fad9-4aa7-c2dc-9439b9ffe180"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__________________________________________________________________________________________________\n",
      "Layer (type)                    Output Shape         Param #     Connected to                     \n",
      "==================================================================================================\n",
      "input_1 (InputLayer)            (None, 1)            0                                            \n",
      "__________________________________________________________________________________________________\n",
      "dense (Dense)                   (None, 2)            4           input_1[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dense_1 (Dense)                 (None, 8)            24          dense[0][0]                      \n",
      "__________________________________________________________________________________________________\n",
      "reshape (Reshape)               (None, 4, 2)         0           dense_1[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "lambda_1 (Lambda)               (None, 4, 2)         0           reshape[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "lambda (Lambda)                 (None, 4, 2)         0           reshape[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "lambda_2 (Lambda)               ()                   0           lambda_1[0][0]                   \n",
      "                                                                 lambda[0][0]                     \n",
      "==================================================================================================\n",
      "Total params: 28\n",
      "Trainable params: 28\n",
      "Non-trainable params: 0\n",
      "__________________________________________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "model_policy = keras.models.Model(inputs=tx_inp, outputs=policy)\n",
    "model_tx = keras.models.Model(inputs=tx_inp, outputs=xp)\n",
    "model_x = keras.models.Model(inputs=tx_inp, outputs=x)\n",
    "\n",
    "model_policy.compile(loss=loss_tx, optimizer=tf.keras.optimizers.SGD(lr = 1e-5))\n",
    "print(model_policy.summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "BL2rg2ODob7G"
   },
   "source": [
    "# Receiver\n",
    "\n",
    "## Defining Architecture"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 289
    },
    "colab_type": "code",
    "id": "jPJKGpLLLqbo",
    "outputId": "f29679e4-cbce-4538-f71c-255e010c600e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input_2 (InputLayer)         (None, 4, 2)              0         \n",
      "_________________________________________________________________\n",
      "reshape_1 (Reshape)          (None, 8)                 0         \n",
      "_________________________________________________________________\n",
      "dense_2 (Dense)              (None, 40)                360       \n",
      "_________________________________________________________________\n",
      "dense_3 (Dense)              (None, 2)                 82        \n",
      "=================================================================\n",
      "Total params: 442\n",
      "Trainable params: 442\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "rx_inp = Input((channel,2))\n",
    "to_flat = Reshape((2*channel,))(rx_inp)\n",
    "fc = Dense(5*2*channel, activation = 'relu')(to_flat)\n",
    "softmax = Dense(msg_total, activation = 'softmax')(fc)\n",
    "\n",
    "model_rx = keras.models.Model(inputs=rx_inp, outputs=softmax)\n",
    "\n",
    "model_rx.compile(loss=tf.keras.losses.categorical_crossentropy, optimizer=tf.keras.optimizers.Adam())\n",
    "print(model_rx.summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "xP7QACNwogGC"
   },
   "source": [
    "## Alternative Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 938
    },
    "colab_type": "code",
    "id": "g6h-Vx6JpksG",
    "outputId": "8594d015-b5aa-497f-dfdf-49aa46c21e65"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use tf.cast instead.\n",
      "epoch:  0 tx_loss 6.355036735534668 rx_loss 0.7242905497550964\n",
      "epoch:  100 tx_loss 1.8137860298156738 rx_loss 0.5351258516311646\n",
      "epoch:  200 tx_loss 0.8131426572799683 rx_loss 0.31978639960289\n",
      "epoch:  300 tx_loss 0.4941194951534271 rx_loss 0.1713171899318695\n",
      "epoch:  400 tx_loss 0.3641790747642517 rx_loss 0.09444549679756165\n",
      "epoch:  500 tx_loss 0.7153945565223694 rx_loss 0.054864201694726944\n",
      "epoch:  600 tx_loss 0.4647824764251709 rx_loss 0.04075027257204056\n",
      "epoch:  700 tx_loss 0.4751901626586914 rx_loss 0.025828994810581207\n",
      "epoch:  800 tx_loss 0.5345849990844727 rx_loss 0.02043948322534561\n",
      "epoch:  900 tx_loss 0.3673986792564392 rx_loss 0.014428166672587395\n",
      "epoch:  1000 tx_loss 0.3670782148838043 rx_loss 0.011922061443328857\n",
      "epoch:  1100 tx_loss 0.9511679410934448 rx_loss 0.00840630941092968\n",
      "epoch:  1200 tx_loss 0.6923137903213501 rx_loss 0.007425171323120594\n",
      "epoch:  1300 tx_loss 0.741775393486023 rx_loss 0.00629937369376421\n",
      "epoch:  1400 tx_loss 0.9444401860237122 rx_loss 0.005360973533242941\n",
      "epoch:  1500 tx_loss 1.7412992715835571 rx_loss 0.004391422029584646\n",
      "epoch:  1600 tx_loss 0.6625553369522095 rx_loss 0.00392648670822382\n",
      "epoch:  1700 tx_loss 0.8560501933097839 rx_loss 0.0038145408034324646\n",
      "epoch:  1800 tx_loss 0.7468227744102478 rx_loss 0.0030808132141828537\n",
      "epoch:  1900 tx_loss 0.7250442504882812 rx_loss 0.002631203504279256\n",
      "epoch:  2000 tx_loss 0.528317928314209 rx_loss 0.002227142918854952\n",
      "epoch:  2100 tx_loss 0.42298823595046997 rx_loss 0.0019248031312599778\n",
      "epoch:  2200 tx_loss 0.3724147081375122 rx_loss 0.0018200164195150137\n",
      "epoch:  2300 tx_loss 0.598807692527771 rx_loss 0.0014720705803483725\n",
      "epoch:  2400 tx_loss 0.570252537727356 rx_loss 0.0015578294405713677\n",
      "epoch:  2500 tx_loss 1.3871800899505615 rx_loss 0.0013590501621365547\n",
      "epoch:  2600 tx_loss 0.4627700746059418 rx_loss 0.001310709398239851\n",
      "epoch:  2700 tx_loss 1.3259549140930176 rx_loss 0.001097007654607296\n",
      "epoch:  2800 tx_loss 1.886702537536621 rx_loss 0.0010604213457554579\n",
      "epoch:  2900 tx_loss 1.357552409172058 rx_loss 0.0008800771902315319\n",
      "epoch:  3000 tx_loss 0.5879579782485962 rx_loss 0.0009826362365856767\n",
      "epoch:  3100 tx_loss 1.249929666519165 rx_loss 0.0008248339290730655\n",
      "epoch:  3200 tx_loss 0.49422529339790344 rx_loss 0.0007180306711234152\n",
      "epoch:  3300 tx_loss 0.7422264814376831 rx_loss 0.0005964445881545544\n",
      "epoch:  3400 tx_loss 1.3139021396636963 rx_loss 0.0006083508487790823\n",
      "epoch:  3500 tx_loss 0.5861632823944092 rx_loss 0.0004838921595364809\n",
      "epoch:  3600 tx_loss 0.1856534779071808 rx_loss 0.0006139599718153477\n",
      "epoch:  3700 tx_loss 0.494204044342041 rx_loss 0.00048443354899063706\n",
      "epoch:  3800 tx_loss 0.6790932416915894 rx_loss 0.0004982298123650253\n",
      "epoch:  3900 tx_loss 0.8209909200668335 rx_loss 0.0003675811749417335\n",
      "epoch:  4000 tx_loss 0.7746607065200806 rx_loss 0.00046498404117301106\n",
      "epoch:  4100 tx_loss 0.8726150989532471 rx_loss 0.00035181891871616244\n",
      "epoch:  4200 tx_loss 0.4192732572555542 rx_loss 0.0003383115981705487\n",
      "epoch:  4300 tx_loss 1.2453705072402954 rx_loss 0.0003893995308317244\n",
      "epoch:  4400 tx_loss 1.0274486541748047 rx_loss 0.0003541375626809895\n",
      "epoch:  4500 tx_loss 0.4736355245113373 rx_loss 0.00022588712454307824\n",
      "epoch:  4600 tx_loss 0.7548935413360596 rx_loss 0.0003139497130177915\n",
      "epoch:  4700 tx_loss 0.8765056133270264 rx_loss 0.0002393309841863811\n",
      "epoch:  4800 tx_loss 0.9064711332321167 rx_loss 0.00023339706240221858\n",
      "epoch:  4900 tx_loss 0.5094815492630005 rx_loss 0.00019088402041234076\n"
     ]
    }
   ],
   "source": [
    "loss_tx = []\n",
    "loss_rx = []\n",
    "for epoch in range(epochs):\n",
    "#     Transmitter training\n",
    "    raw_input = np.random.randint(0,msg_total,(batch_size))\n",
    "    label = np.zeros((batch_size, msg_total))\n",
    "    label[np.arange(batch_size), raw_input] = 1\n",
    "    tx_input = raw_input/float(msg_total)\n",
    "    xp = model_tx.predict(tx_input)\n",
    "    y = xp + np.random.normal(0,0.001,(batch_size, channel,2))\n",
    "    pred = model_rx.predict(y)\n",
    "    loss = np.sum(np.square(label - pred), axis = 1)\n",
    "    history_tx = model_policy.fit(tx_input, loss, batch_size=batch_size, epochs=1, verbose=0)    \n",
    "    loss_tx.append(history_tx.history['loss'][0])\n",
    "    \n",
    "#     Receiver Training\n",
    "    raw_input = np.random.randint(0,msg_total,(batch_size))\n",
    "    label = np.zeros((batch_size, msg_total))\n",
    "    label[np.arange(batch_size), raw_input] = 1\n",
    "    tx_input = raw_input/float(msg_total)\n",
    "    x = model_x.predict(tx_input)\n",
    "    y = x + np.random.normal(0,0.001,(batch_size, channel,2))\n",
    "    history_rx = model_rx.fit(y, label, batch_size=batch_size, epochs=1, verbose=0)\n",
    "    loss_rx.append(history_rx.history['loss'][0])\n",
    "    \n",
    "    if(epoch % 100 == 0):\n",
    "        print('epoch: ', epoch, 'tx_loss', history_tx.history['loss'][0], 'rx_loss', history_rx.history['loss'][0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "-r0lcWXhor_o"
   },
   "source": [
    "## Plotting Transmitter and Receiver Losses"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 350
    },
    "colab_type": "code",
    "id": "YOax3T0THnrF",
    "outputId": "ed96b1ce-1dcd-4495-c0de-02bb11b959db"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 864x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "tags": []
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize = (12,5))\n",
    "plt.subplot(1,2,1)\n",
    "plt.plot(loss_tx)\n",
    "plt.title('Transmitter Loss')\n",
    "plt.xlabel('epochs')\n",
    "plt.ylabel('loss')\n",
    "plt.subplot(1,2,2)\n",
    "plt.plot(loss_rx)\n",
    "plt.title('Receiver Loss')\n",
    "plt.xlabel('epochs')\n",
    "plt.ylabel('loss')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "bJKYMpkFpD6K"
   },
   "source": [
    "## Prediction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 136
    },
    "colab_type": "code",
    "id": "Ob52RzJevSF2",
    "outputId": "e9ebfe7c-daaf-4556-cda6-a6d6e608eecf"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1 0 1 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0\n",
      " 0 0 1 0 0 1 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 0 0 1 1 0 1 0 1 1 0 1 1 0 0 0\n",
      " 0 1 0 0 1 1 1 0 1 0 0 1 1 0 0 0 0 1 0 0 0 1 0 1 0 1]\n",
      "[1 0 1 0 1 1 1 1 1 1 0 0 0 1 1 0 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0\n",
      " 0 0 1 0 0 1 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 0 0 1 1 0 1 0 1 1 0 1 1 0 0 0\n",
      " 0 1 0 0 1 1 1 0 1 0 0 1 1 0 0 0 0 1 0 0 0 1 0 1 0 1]\n",
      "accuracy: 1.0\n"
     ]
    }
   ],
   "source": [
    "#testing\n",
    "batch_size = 100\n",
    "raw_input = np.random.randint(0,msg_total,(batch_size))\n",
    "print(raw_input)\n",
    "label = np.zeros((batch_size, msg_total))\n",
    "label[np.arange(batch_size), raw_input] = 1\n",
    "tx_input = raw_input/float(msg_total)\n",
    "xp = model_x.predict(tx_input)\n",
    "y = xp + np.random.normal(0,0.001,(batch_size, channel,2))\n",
    "pred = model_rx.predict(y)\n",
    "pred_int = np.argmax(pred, axis = 1)\n",
    "print(pred_int)\n",
    "\n",
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "print('accuracy:',accuracy_score(raw_input, pred_int))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "v0SWR8noCoc6"
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "include_colab_link": true,
   "name": "2_pam_model.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
